Skip to content

feat: Vault Transit secrets engine signer support for Stellar#801

Open
samkirton wants to merge 2 commits into
OpenZeppelin:mainfrom
samkirton:stellar-vault-transit-signer
Open

feat: Vault Transit secrets engine signer support for Stellar#801
samkirton wants to merge 2 commits into
OpenZeppelin:mainfrom
samkirton:stellar-vault-transit-signer

Conversation

@samkirton

@samkirton samkirton commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Add HashiCorp Vault Transit Support for Stellar Signing (#800)

Summary

This proposal adds HashiCorp Vault Transit support to Stellar relayers in OpenZeppelin Relayer.

  • Vault Transit-backed signer integration for Stellar transaction signing
  • Support for Stellar account derivation and validation from Vault-managed Ed25519 public keys
  • An end-to-end example demonstrating deployment and usage of Vault Transit-backed Stellar signing

Implementation

  • Vault Transit signer implementation for Stellar transaction signing
  • Stellar account derivation from Vault Transit Ed25519 public keys
  • Integration with existing relayer signer abstractions
  • Reuse of existing Vault Transit authentication and key management flows
  • Relayer configuration support for:
    • Vault address
    • AppRole credentials
    • Transit key name
    • Stellar public key
    • vault_transit signer selection

Documentation & Examples

  • Vault Transit setup and configuration
  • Ed25519 Transit key creation
  • Policy configuration
  • AppRole authentication
  • Relayer configuration examples
  • Docker Compose deployment with Vault, Redis, and Relayer
  • Stellar Testnet transaction submission walkthrough

Notes

The implementation is designed to align with the existing Vault Transit integration already available for Solana, minimising integration complexity while extending support for Stellar’s Ed25519 signing requirements.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Vault Transit signer support for Stellar using Ed25519 keys.
    • New ready-to-use example configuration for Vault Transit-based Stellar transaction signing.
  • Documentation

    • Updated signer support documentation to include Vault Transit compatibility.
    • Added comprehensive setup guide and troubleshooting section for Vault Transit Stellar signing.

@samkirton samkirton requested a review from a team as a code owner June 15, 2026 14:29
@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5b488af3-f460-4fd3-a445-a9fc68bfdfdb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds a VaultTransitSigner for Stellar that delegates Ed25519 signing to HashiCorp Vault Transit without exposing the private key. The signer is wired into the StellarSigner enum and factory. A complete Docker Compose example with Vault, Redis, and relayer services is included alongside documentation updates across signers, Stellar, and index pages.

Changes

Stellar Vault Transit Signer

Layer / File(s) Summary
VaultTransitSigner core implementation and tests
src/services/signer/stellar/vault_transit_signer.rs
Defines VaultTransitSigner<T> struct with constructors; implements address derivation from base64 public key, SHA-256 payload hashing, Vault Transit signing call, signature normalization (stripping vault:v1: prefix), 64-byte validation, and DecoratedSignature construction; implements Signer and StellarSignTrait traits; adds async unit tests with a mock vault service covering success and error paths.
StellarSigner enum variant and factory wiring
src/services/signer/stellar/mod.rs
Declares the vault_transit_signer module, adds VaultTransit(VaultTransitSigner) to the StellarSigner enum, extends address/sign_transaction/sign_xdr_transaction match arms to dispatch to the new signer, and implements SignerConfig::VaultTransit in StellarSignerFactory to construct a VaultService with "transit" mount path.
Docker Compose example: services, config, and env
examples/stellar-vault-transit-signer/docker-compose.yaml, examples/stellar-vault-transit-signer/config/config.json, examples/stellar-vault-transit-signer/.env.example
Three-service Compose file (relayer, redis, vault in dev mode at port 8200); relayer config.json wiring vault_transit signer to Vault AppRole credentials from env vars; .env.example template with REDIS_URL, API_KEY, WEBHOOK_SIGNING_KEY, VAULT_ROLE_ID, VAULT_SECRET_ID.
Example README and documentation updates
examples/stellar-vault-transit-signer/README.md, docs/configuration/signers.mdx, docs/stellar.mdx, docs/index.mdx, README.md
Step-by-step example README covering Vault setup, key/policy/AppRole creation, relayer configuration, run commands, test requests, and troubleshooting. Updates signers.mdx compatibility matrix (Solana/Stellar now supported), pubkey field guidance, and a new "Vault Transit signing failures" troubleshooting subsection; adds Vault Transit and AWS KMS to stellar.mdx signer support list; adds example entries to docs/index.mdx and root README.md.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Suggested reviewers

  • zeljkoX

🐇 A key in the vault, no secret in sight,
Ed25519 signs in the dark of the night.
The relayer hops forward, Stellar in tow,
With transit and AppRole, the transactions flow.
From base64 hints to decorated delight—
This bunny approves of your cryptographic might! 🔐✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is missing the required 'Testing Process' section and lacks clear reference to the related issue (#800) as a separate link. Add a 'Testing Process' section describing how the changes were tested, and ensure the related issue is clearly referenced per the template's requirement.
Docstring Coverage ⚠️ Warning Docstring coverage is 19.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately and concisely summarizes the main feature addition: Vault Transit secrets engine signer support for Stellar transactions.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.60870% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.54%. Comparing base (bfe544c) to head (afbf76b).

Files with missing lines Patch % Lines
...rc/services/signer/stellar/vault_transit_signer.rs 86.06% 40 Missing ⚠️
src/services/signer/stellar/mod.rs 0.00% 12 Missing ⚠️
Additional details and impacted files
Flag Coverage Δ
ai 0.00% <0.00%> (ø)
dev 89.54% <82.60%> (-0.02%) ⬇️
properties 0.01% <0.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

@@            Coverage Diff             @@
##             main     #801      +/-   ##
==========================================
- Coverage   89.55%   89.54%   -0.02%     
==========================================
  Files         301      302       +1     
  Lines      127697   127995     +298     
==========================================
+ Hits       114360   114610     +250     
- Misses      13337    13385      +48     
Files with missing lines Coverage Δ
src/services/signer/stellar/mod.rs 51.66% <0.00%> (-5.22%) ⬇️
...rc/services/signer/stellar/vault_transit_signer.rs 86.06% <86.06%> (ø)

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@samkirton samkirton changed the title Vault Transit secrets engine signer support for Stellar feat: Vault Transit secrets engine signer support for Stellar Jun 15, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/services/signer/stellar/vault_transit_signer.rs (1)

41-46: Convert constructor to return Result for graceful error handling.

VaultTransitSigner::new panics on missing config via .expect() at line 45. Convert it to return Result and propagate the error through the factory methods, both of which already handle Result types:

  • stellar/mod.rs:221: Factory wraps result in Ok()
  • solana/mod.rs:278: Factory already returns Result

This aligns with the Rust guideline: "Prefer Result over panic, use ? operator for error propagation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/services/signer/stellar/vault_transit_signer.rs` around lines 41 - 46,
The VaultTransitSigner::new constructor currently panics via .expect() when
vault transit config is missing instead of gracefully handling the error. Change
the method signature to return Result<Self, Error> (or appropriate error type),
replace the .expect() call with the ? operator or match statement to propagate
the error, and then update both factory methods—the one in stellar/mod.rs at
line 221 that currently wraps the result in Ok() and the one in solana/mod.rs at
line 278 that already returns Result—to properly handle the new Result-returning
constructor by using the ? operator to propagate the error or wrapping as needed
to maintain the factory's Result return type.

Source: Coding guidelines

examples/stellar-vault-transit-signer/docker-compose.yaml (1)

27-29: ⚡ Quick win

Add Vault to service dependency ordering for deterministic startup.

The relayer example depends on Vault for signer auth/signing, but depends_on only lists Redis. Add Vault to reduce first-boot failures/race conditions in the example stack.

Suggested patch
     depends_on:
       - redis
+      - vault
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/stellar-vault-transit-signer/docker-compose.yaml` around lines 27 -
29, The relayer service in the docker-compose.yaml file has a depends_on section
that only lists redis, but the relayer also depends on Vault for signer
authentication and signing operations. Add vault to the depends_on list
alongside redis to ensure Vault is started before the relayer service,
preventing race conditions and first-boot failures. Update the depends_on block
to include both redis and vault as dependencies.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/stellar.mdx`:
- Around line 536-538: The Vault Transit example link in the documentation
points to an incorrect path that does not match the Stellar example directory
structure added in this changeset. Update the vault-transit-signer example link
to reference the correct location of the Stellar Vault Transit example directory
so that readers are directed to the correct example code.

In `@examples/stellar-vault-transit-signer/config/config.json`:
- Around line 32-44: The `pubkey` field in the vault_transit configuration is
set to an empty string, but the `VaultTransitSignerConfig` validation in
src/models/signer/mod.rs requires it to be a non-empty, valid base64-encoded
Ed25519 public key. Replace the empty string value with a valid base64-encoded
Ed25519 public key that corresponds to the signing key specified in the
`key_name` field to satisfy the validation requirements and allow the example to
start successfully.

In `@examples/stellar-vault-transit-signer/docker-compose.yaml`:
- Around line 50-54: The Vault service in the docker-compose.yaml file is
exposing port 8200 on all host interfaces (0.0.0.0) while using a fixed
development token, creating a security vulnerability in shared/dev-network
environments. Modify the port binding in the ports section from the format that
exposes to all interfaces to one that restricts the binding to localhost only,
ensuring the Vault API is accessible only from the local machine where the
container is running.

In `@examples/stellar-vault-transit-signer/README.md`:
- Around line 38-40: The VAULT_ADDR environment variable example in the README
uses 0.0.0.0 as the host address, which is a listen/bind address and not
appropriate for client connections. Replace 0.0.0.0 with 127.0.0.1 or localhost
in the VAULT_ADDR export statement to provide a correct and reliable example
that works across different system configurations.

---

Nitpick comments:
In `@examples/stellar-vault-transit-signer/docker-compose.yaml`:
- Around line 27-29: The relayer service in the docker-compose.yaml file has a
depends_on section that only lists redis, but the relayer also depends on Vault
for signer authentication and signing operations. Add vault to the depends_on
list alongside redis to ensure Vault is started before the relayer service,
preventing race conditions and first-boot failures. Update the depends_on block
to include both redis and vault as dependencies.

In `@src/services/signer/stellar/vault_transit_signer.rs`:
- Around line 41-46: The VaultTransitSigner::new constructor currently panics
via .expect() when vault transit config is missing instead of gracefully
handling the error. Change the method signature to return Result<Self, Error>
(or appropriate error type), replace the .expect() call with the ? operator or
match statement to propagate the error, and then update both factory methods—the
one in stellar/mod.rs at line 221 that currently wraps the result in Ok() and
the one in solana/mod.rs at line 278 that already returns Result—to properly
handle the new Result-returning constructor by using the ? operator to propagate
the error or wrapping as needed to maintain the factory's Result return type.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1646542d-c217-4943-94bb-4f000fce933a

📥 Commits

Reviewing files that changed from the base of the PR and between ef5d08b and e52d63d.

📒 Files selected for processing (10)
  • README.md
  • docs/configuration/signers.mdx
  • docs/index.mdx
  • docs/stellar.mdx
  • examples/stellar-vault-transit-signer/.env.example
  • examples/stellar-vault-transit-signer/README.md
  • examples/stellar-vault-transit-signer/config/config.json
  • examples/stellar-vault-transit-signer/docker-compose.yaml
  • src/services/signer/stellar/mod.rs
  • src/services/signer/stellar/vault_transit_signer.rs

Comment thread docs/stellar.mdx
Comment on lines 536 to +538
For complete examples:
- Vault Transit: [vault-transit-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/vault-transit-signer)
- AWS KMS: [aws-kms-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/aws-kms-signer)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix Vault Transit example link target.

The listed Vault Transit example path does not match the Stellar example directory added in this change set, so readers will be sent to the wrong location.

Suggested patch
-- Vault Transit: [vault-transit-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/vault-transit-signer)
+- Vault Transit: [stellar-vault-transit-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/stellar-vault-transit-signer)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
For complete examples:
- Vault Transit: [vault-transit-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/vault-transit-signer)
- AWS KMS: [aws-kms-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/aws-kms-signer)
For complete examples:
- Vault Transit: [stellar-vault-transit-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/stellar-vault-transit-signer)
- AWS KMS: [aws-kms-signer example](https://github.com/OpenZeppelin/openzeppelin-relayer/tree/main/examples/aws-kms-signer)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/stellar.mdx` around lines 536 - 538, The Vault Transit example link in
the documentation points to an incorrect path that does not match the Stellar
example directory structure added in this changeset. Update the
vault-transit-signer example link to reference the correct location of the
Stellar Vault Transit example directory so that readers are directed to the
correct example code.

Comment on lines +32 to +44
"config": {
"address": "http://vault:8200",
"role_id": {
"type": "env",
"value": "VAULT_ROLE_ID"
},
"secret_id": {
"type": "env",
"value": "VAULT_SECRET_ID"
},
"key_name": "my_signing_key",
"pubkey": ""
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

vault_transit example config is invalid with empty pubkey.

Line 43 sets "pubkey": "", but VaultTransitSignerConfig.pubkey is validated as non-empty and signer derivation also expects a valid base64 Ed25519 public key. This will fail config validation/startup for the example.

Suggested fix
-        "pubkey": ""
+        "pubkey": "REPLACE_WITH_BASE64_ED25519_PUBLIC_KEY"

Based on the provided config contract, pubkey must be present/non-empty in src/models/signer/mod.rs (VaultTransitSignerConfig validation).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"config": {
"address": "http://vault:8200",
"role_id": {
"type": "env",
"value": "VAULT_ROLE_ID"
},
"secret_id": {
"type": "env",
"value": "VAULT_SECRET_ID"
},
"key_name": "my_signing_key",
"pubkey": ""
}
"config": {
"address": "http://vault:8200",
"role_id": {
"type": "env",
"value": "VAULT_ROLE_ID"
},
"secret_id": {
"type": "env",
"value": "VAULT_SECRET_ID"
},
"key_name": "my_signing_key",
"pubkey": "REPLACE_WITH_BASE64_ED25519_PUBLIC_KEY"
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/stellar-vault-transit-signer/config/config.json` around lines 32 -
44, The `pubkey` field in the vault_transit configuration is set to an empty
string, but the `VaultTransitSignerConfig` validation in
src/models/signer/mod.rs requires it to be a non-empty, valid base64-encoded
Ed25519 public key. Replace the empty string value with a valid base64-encoded
Ed25519 public key that corresponds to the signing key specified in the
`key_name` field to satisfy the validation requirements and allow the example to
start successfully.

Comment on lines +50 to +54
ports:
- 8200:8200
environment:
- VAULT_DEV_ROOT_TOKEN_ID=dev-only-token
cap_add:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restrict Vault dev API exposure to localhost.

The Vault service uses a fixed dev root token and publishes 8200 on all host interfaces. That combination is unsafe for shared/dev-network environments; bind to loopback only.

Suggested patch
   vault:
     image: hashicorp/vault:1.19.0
     platform: linux/amd64
     ports:
-      - 8200:8200
+      - 127.0.0.1:8200:8200
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ports:
- 8200:8200
environment:
- VAULT_DEV_ROOT_TOKEN_ID=dev-only-token
cap_add:
ports:
- 127.0.0.1:8200:8200
environment:
- VAULT_DEV_ROOT_TOKEN_ID=dev-only-token
cap_add:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/stellar-vault-transit-signer/docker-compose.yaml` around lines 50 -
54, The Vault service in the docker-compose.yaml file is exposing port 8200 on
all host interfaces (0.0.0.0) while using a fixed development token, creating a
security vulnerability in shared/dev-network environments. Modify the port
binding in the ports section from the format that exposes to all interfaces to
one that restricts the binding to localhost only, ensuring the Vault API is
accessible only from the local machine where the container is running.

Comment on lines +38 to +40
```bash
export VAULT_ADDR='http://0.0.0.0:8200'
export VAULT_TOKEN='dev-only-token'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use localhost for VAULT_ADDR in CLI examples.

0.0.0.0 is a listen/bind address, not the recommended client destination. This can fail on some setups; use 127.0.0.1 (or localhost) in the command example.

Suggested patch
-export VAULT_ADDR='http://0.0.0.0:8200'
+export VAULT_ADDR='http://127.0.0.1:8200'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```bash
export VAULT_ADDR='http://0.0.0.0:8200'
export VAULT_TOKEN='dev-only-token'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/stellar-vault-transit-signer/README.md` around lines 38 - 40, The
VAULT_ADDR environment variable example in the README uses 0.0.0.0 as the host
address, which is a listen/bind address and not appropriate for client
connections. Replace 0.0.0.0 with 127.0.0.1 or localhost in the VAULT_ADDR
export statement to provide a correct and reliable example that works across
different system configurations.

@samkirton samkirton force-pushed the stellar-vault-transit-signer branch from afbf76b to 3d1911b Compare June 22, 2026 20:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant